#include "EnglishName.h"
#include <QStringList>

// remove and return the last name
QString EnglishName::takeLastName(QString& fullName)
{
	QString result;
	int comma = fullName.indexOf(',');    // find ','
	if(comma > -1)
	{
		result = fullName.left(comma);    // left of comma must be the last name
		fullName.remove(0, comma+1);
		fullName = fullName.simplified(); // remove unnecessary spaces
	}
	else                                  // no ','
	{
		// find last . or space
		int i = fullName.length()-1;
		while(i >= 0 && fullName.at(i) != ' ' && fullName.at(i) != '.')
			i--;
		result = fullName.right(fullName.length() - i - 1);   // take the right part
		fullName.chop(result.length());
		fullName = fullName.simplified();    // remove unnecessary spaces
	}
	return result;
}

// remove and return the first name
QString EnglishName::takeFirstName(QString& fullName)
{
	QString result;

	// find the first . or space
	int i = 0;
	while(i < fullName.length() && fullName.at(i) != '.' && fullName.at(i) != ' ')
		i++;
	result = fullName.left(i+1).simplified();   // take the left part
	fullName.remove(0, i+1);
	fullName = fullName.simplified();           // remove unnecessary spaces
	return result;
}

EnglishName::CompareResult EnglishName::compareSections(const QString& lhs, const QString& rhs)
{
	if(lhs.isEmpty() && rhs.isEmpty())    // both empty
		return SAME;
	if(lhs.isEmpty() || rhs.isEmpty())    // either side is empty
		return SIMILAR;

	// both are not empty
	bool leftShort  = lhs.contains('.');  // short look like: X.
	bool rightShort = rhs.contains('.');
	if( (leftShort && rightShort) || (!leftShort && !rightShort) )  // same type
		return lhs.compare(rhs, Qt::CaseInsensitive) == 0 ? SAME : NOTSAME;
	if(leftShort && !rightShort)                                    // one is short
		return rhs.startsWith(lhs.at(0)) ? SIMILAR : NOTSAME;
	if(!leftShort && rightShort)
		return lhs.startsWith(rhs.at(0)) ? SIMILAR : NOTSAME;
	return NOTSAME;
}

bool EnglishName::compare(const QString& lhs, const QString& rhs)
{
	EnglishName name1(lhs);
	EnglishName name2(rhs);
	if(name1.last != name2.last)   // last name must be the same
		return false;

	// then compare first names
	CompareResult compareFirst = compareSections(name1.first, name2.first);

	// if can't determine by first name
	// similar mid name considered the same
	if(compareFirst == SIMILAR)
		return compareSections(name1.middle, name2.middle) == NOTSAME ? false : true;

    return compareFirst == SAME;   // if first name can draw the conclusion
}

EnglishName::EnglishName(const QString& fullName)
{
    QString name(fullName.simplified());
	last   = takeLastName(name);
	first  = takeFirstName(name);
	middle = name;
}

// L: last name
// F: first name
// f: first name initial
// M: middle name
// m: middle name initial
// L,;m;F -> Chen, W. Cong
// F;;L -> Cong Chen
QString EnglishName::toString(const QString& formatString) const
{
    QStringList formats = formatString.split(";");
    if(formats.size() != 3)
        return "FormatError!";

    QStringList sections;
    foreach(const QString& format, formats)
        if(!format.isEmpty())
        {
            QString section = formatSection(format);
            if(!section.isEmpty())
                sections << section;
        }
    return sections.join(" ");
}

QString EnglishName::toInitial(const QString& name) const {
    return name.isEmpty() ? QString() : QString(name.at(0).toUpper()) + ".";
}

QString EnglishName::formatSection(const QString& format) const
{
    QString result;
    foreach(QChar ch, format)
    {
        if(ch == 'L')
            result.append(last);
        else if(ch == 'F')
            result.append(first);
        else if(ch == 'f')
            result.append(toInitial(first));
        else if(ch == 'M')
            result.append(middle);
        else if(ch == 'm')
            result.append(toInitial(middle));
        else
            result.append(ch);
    }
    return result;
}

